﻿#include "PatrolServer.h"
#include "configView.h"

// 使用 utf8 编码
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

QString		g_strSaveImageDir;			// 存储图片的路径 
QString		g_strReleaseImageDir;		// 存储图片的路径 
QList<QString >							g_Whitelist;


PatrolServer::PatrolServer(QWidget* parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);


	QString runPath = QCoreApplication::applicationDirPath();       //获取exe路径
	m_strConfigPath = runPath + "/config.ini";

	QString buildTime = QStringLiteral("%1 %2").arg(__DATE__).arg(__TIME__);
	QDateTime dateTime = QLocale("en_US").toDateTime(buildTime.simplified(), "MMM d yyyy hh:mm:ss");

	this->setWindowTitle("PatrolServer - 启源芯动力 AI 巡检系统（图像）			buildTime " + dateTime.toString("yyyy/MM/dd  hh:mm:ss"));

	// 字典初始化
	m_MapNames.insert("battery_shell_broken",			"1.蒙皮破损");
	m_MapNames.insert("battery_kettle_broken",			"2.水壶破损");
	m_MapNames.insert("battery_top_invade",				"3.顶部异物");
	m_MapNames.insert("battery_frame_deform",			"4.框架变形");
	m_MapNames.insert("battery_tilt",					"5.电池倾斜");

	m_MapNames.insert("jiuxing_steel_rope_broken",		"6.玖行断股");
	m_MapNames.insert("jiuxing_rope_coiler_failure",	"7.玖行卷绳");

	m_MapNames.insert("rongqing_steel_rope_broken",		"8.融青断股");
	m_MapNames.insert("rongqing_oil_leakage",			"9.融青漏油");
		
	m_MapNames.insert("test-topic",						"10.test_kafka");
	
	// 初始化状态栏
	m_pKafkaState = new QLabel("kafka error", this);
	m_pKafkaState->setMinimumWidth(this->width());
	m_pKafkaState->setStyleSheet("QLabel{background-color:rgb(200,10,10);}");

	// 将初始化的标签添加到底部状态栏上
	ui.statusBar->addWidget(m_pKafkaState);


	// 恢复系统设置参数
	g_strSaveImageDir = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "SaveImageDir", "E://启源芯动力//全部图片//");
	g_strReleaseImageDir = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "ReleaseImageDir", "E://启源芯动力//");



	// 获取主机名
	QString machineName = QHostInfo::localHostName();

	/// 创建 kafka 对象
	m_pKafka = new kafka(KAFKA_IP, machineName);




	// 创建检测对象
	m_pFrame_deform = new battery_frame_deform;

	// 水壶检测
	m_pKettle_broken = new battery_kettle_broken;
	m_strKettleDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "KettleDetModelPath", "");
	m_strKettleRectDefectModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "KettleRectDefectModelPath", "");
	m_pKettle_broken->m_strKettleDetModelPath = m_strKettleDetModelPath;
	m_pKettle_broken->m_strKettleRectDefectModelPath = m_strKettleRectDefectModelPath;

	m_pKettle_broken->init();

	m_pShell_broken = new battery_shell_broken;
	m_strShellDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "ShellDetModelPath", "");
	m_strShellRectDefectModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "ShellRectDefectModelPath", "");
	m_strShellRectClassModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "ShellRectClassModelPath", "");
	m_pShell_broken->m_strShellDetModelPath = m_strShellDetModelPath;
	m_pShell_broken->m_strShellRectDefectModelPath = m_strShellRectDefectModelPath;
	m_pShell_broken->m_strShellRectClassModelPath = m_strShellRectClassModelPath;
	m_pShell_broken->init();


	m_pFrame_tilt = new battery_tilt;


	m_pTop_invade = new battery_top_invade;
	m_strTopDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "TopDetModelPath", "");
	m_strTopRectDefectModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "TopRectDefectModelPath", "");
	m_strTopRectClassModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "TopRectClassModelPath", "");
	m_pTop_invade->m_strTopDetModelPath = m_strTopDetModelPath;
	m_pTop_invade->m_strTopRectDefectModelPath = m_strTopRectDefectModelPath;
	m_pTop_invade->m_strTopRectClassModelPath = m_strTopRectClassModelPath;
	m_pTop_invade->init();


	m_RQ_steel_rope_broken = new rongqing_steel_rope_broken;
	m_strRQ_SteelDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_SteelDetModelPath", "");
	m_strRQ_SteelRectDefectModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_SteelRectDefectModelPath", "");
	m_RQ_steel_rope_broken->m_strRQ_SteelDetModelPath = m_strRQ_SteelDetModelPath;
	m_RQ_steel_rope_broken->m_strRQ_SteelRectDefectModelPath = m_strRQ_SteelRectDefectModelPath;
	m_RQ_steel_rope_broken->init();



	m_pRQ_oil_leakage = new rongqing_oil_leakage;
	m_strRQ_MotorDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_MotorDetModelPath", "");
	m_strRQ_FloorDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_FloorDetModelPath", "");
	m_pRQ_oil_leakage->m_strRQ_MotorDetModelPath = m_strRQ_MotorDetModelPath;
	m_pRQ_oil_leakage->m_strRQ_FloorDetModelPath = m_strRQ_FloorDetModelPath;

	m_strRQ_MotorRectDefectModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_MotorRectDefectModelPath", "");
	m_strRQ_MotorRectClassModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "RQ_MotorRectClassModelPath", "");
	m_pRQ_oil_leakage->m_strRQ_MotorRectDefectModelPath = m_strRQ_MotorRectDefectModelPath;
	m_pRQ_oil_leakage->m_strRQ_MotorRectClassModelPath = m_strRQ_MotorRectClassModelPath;
	m_pRQ_oil_leakage->init();


	m_pJX_steel_rope_broken = new jiuxing_steel_rope_broken;
	m_strJX_SteelDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "JX_SteelDetModelPath", "");
	m_strJX_CableDetModelPath = ConfigUtils::getCfgStr(m_strConfigPath, "Config", "JX_CableDetModelPath", "");
	m_pJX_steel_rope_broken->m_strJX_SteelDetModelPath = m_strJX_SteelDetModelPath;
	m_pJX_steel_rope_broken->init();



	m_pJX_rope_coiler_failure = new jiuxing_rope_coiler_failure;

	m_pJX_rope_coiler_failure->m_strJX_SteelDetModelPath = m_strJX_SteelDetModelPath;
	m_pJX_rope_coiler_failure->m_strJX_CableDetModelPath = m_strJX_CableDetModelPath;
	m_pJX_rope_coiler_failure->init();


	connect(m_pKafka, SIGNAL(valueUpdate(QString, QString)), this, SLOT(on_ValueUpdate(QString, QString)));

	connect(m_pFrame_deform, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pKettle_broken, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pShell_broken, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pFrame_tilt, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pTop_invade, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));

	connect(m_RQ_steel_rope_broken, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pRQ_oil_leakage, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));

	connect(m_pJX_steel_rope_broken, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));
	connect(m_pJX_rope_coiler_failure, SIGNAL(updateShow(int, QImage, int)), this, SLOT(on_updateShow(int, QImage, int)));

	// 检查环境是否支持 ssl，如果不支持，则不启动服务
	if (QSslSocket::supportsSsl())
	{
		// 启动 kafka
		m_pKafka->start();
	}



	// 发送测试代码，验证当前 kafka 是否正常 
	ExampleProducer producer(KAFKA_IP, "test-topic");
	producer.produce("hello.kafka");


	//获取 Qt 所支持的版本
	qDebug() << "OpenSSL支持的版本:" << QSslSocket::sslLibraryBuildVersionString();
	//判断当前 Qt 是否支持 OpenSSL
	qDebug() << "OpenSSL支持情况:" << QSslSocket::supportsSsl();//若输出：OpenSSL支持情况: false，那说明当前 Qt 不支持 OpenSSL

	g_Whitelist.push_back("680101");//115276
	g_Whitelist.push_back("670013");
	g_Whitelist.push_back("100321");
	g_Whitelist.push_back("100526");
	g_Whitelist.push_back("100378");

	g_Whitelist.push_back("100476");
	g_Whitelist.push_back("100366");
	g_Whitelist.push_back("760026");
	g_Whitelist.push_back("680093");
	g_Whitelist.push_back("150907");

	//获取 Qt 所支持的版本
	qDebug() << "OpenSSL支持的版本:" << QSslSocket::sslLibraryBuildVersionString();
	//判断当前 Qt 是否支持 OpenSSL
	qDebug() << "OpenSSL支持情况:" << QSslSocket::supportsSsl();//若输出：OpenSSL支持情况: false，那说明当前 Qt 不支持 OpenSSL


	autoRun();
}

PatrolServer::~PatrolServer()
{
	// 停止 kafka
	m_pKafka->stop();

	m_pFrame_deform->stop();
	m_pKettle_broken->stop();
	m_pShell_broken->stop();
	m_pFrame_tilt->stop();
	m_pTop_invade->stop();

	m_RQ_steel_rope_broken->stop();
	m_pRQ_oil_leakage->stop();

	m_pJX_steel_rope_broken->stop();
	m_pJX_rope_coiler_failure->stop();


	delete m_pFrame_deform;
	delete m_pKettle_broken;
	delete m_pShell_broken;
	delete m_pFrame_tilt;
	delete m_pTop_invade;

	delete m_RQ_steel_rope_broken;
	delete m_pRQ_oil_leakage;

	delete m_pJX_steel_rope_broken;
	delete m_pJX_rope_coiler_failure;


}


void PatrolServer::autoRun()
{
#if _WIN32
#define AUTO_RUN "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"

	QSettings settings(AUTO_RUN, QSettings::NativeFormat);
	QString appPath = QApplication::applicationFilePath();
	QFileInfo fInfo(appPath);
	QString name = fInfo.baseName();    //键-名称
	QString oldPath = settings.value(name).toString(); //获取目前的值-绝对路劲
	QString newPath = QDir::toNativeSeparators(appPath);
	settings.setValue(name, newPath);
#else
	QString appName = QCoreApplication::applicationName();
	QString appPath = QCoreApplication::applicationFilePath();

	// .desktop 文件路径
	QString desktopFilePath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/autostart/" + appName + ".desktop";

	if (enable) {
		// 创建 .desktop 文件
		QFile desktopFile(desktopFilePath);
		if (desktopFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
			QTextStream out(&desktopFile);
			out << "[Desktop Entry]\n";
			out << "Type=Application\n";
			out << "Exec=" << appPath << "\n";
			out << "Hidden=false\n";
			out << "NoDisplay=false\n";
			out << "X-GNOME-Autostart-enabled=true\n";
			out << "Name=" << appName << "\n";
			out << "Comment=Start " << appName << " at login\n";
			desktopFile.close();
		}
#endif
}



/// <summary>
/// 
/// </summary>
/// <param name="strName"></param>
/// <param name="strData"></param>
void PatrolServer::on_ValueUpdate(QString strName, QString strData)
{
	QString strTopicName = strName;
	// 转换为内部名称
	strName = m_MapNames.value(strName);

	if (strName.isEmpty())
	{
		return;
	}

	QJsonParseError jsonError;
	QJsonDocument doc = QJsonDocument::fromJson(strData.toUtf8(), &jsonError);
	// 判断是否解析失败
	if (jsonError.error != QJsonParseError::NoError && !doc.isNull())
	{
		return;
	}


	QJsonObject rootObj = doc.object();

	// 根据键获取值
	QJsonValue image_data = rootObj.value("base64_content").toString();

	// 站名字
	QString station_name = rootObj.value("station_name").toString();

	// 站id
	QString station_id = rootObj.value("station_id").toString();

	// 相机名字
	QString camera_name = rootObj.value("camera_name").toString();

	// 相机id
	QString camera_id = QString::number(rootObj.value("camera_id").toInt());

	// 图片名
	QString picture_name = rootObj.value("picture_path").toString();

	QString device_sn = rootObj.value("device_sn").toString();

	QString server_url = rootObj.value("server_url").toString();

	// http://47.100.83.110:10102/inspect_camera_capture/
	QString strUrl /*= SREVER_URL + station_name + "/" + camera_name + "/" + picture_name*/;

	if (!server_url.isEmpty())
	{
		if (!server_url.contains("http", Qt::CaseSensitive))
		{
			strUrl = "http://" + server_url + "/inspect_camera_capture/" + station_name + "/" + camera_name + "/" + picture_name;
		}
		else
		{
			strUrl = server_url + "/" + station_name + "/" + camera_name + "/" + picture_name;
		}
	}

	// 从字典里解析出当前的 id
	int nID = strName.split(".")[0].toInt();

	IMG_DATA data;
	//	data.image = img;
	data.strTopicName = strTopicName;
	data.nDispId = nID;
	data.strStationName = station_name;
	data.strStationId = station_id;
	data.strCameraName = camera_name;
	data.strCameraId = camera_id;
	data.strPictureName = picture_name;
	data.strName = strName;
	data.strUrl = strUrl;
	data.strDeviceSN = device_sn;


	// 刷新显示
	updateImageView(nID, data);

	// 记录日志

	QString strTime = QDateTime::currentDateTime().toString("MM-dd hh:mm:ss zzz") + "  ";
	QString strCount = QString("%1").arg(ui.listWidget->count(), 4, 10, QLatin1Char('0'));
	QString strLog = strCount + "  " + strTime + station_name + "  " + picture_name;
	ui.listWidget->addItem(strLog.simplified());
	ui.listWidget->scrollToBottom();

}


/// <summary>
/// 
/// </summary>
/// <param name="nId"></param>
/// <param name="img"></param>
/// <param name="strLable"></param>
void PatrolServer::updateImageView(int nId, IMG_DATA data)
{
	QString strTime = QDateTime::currentDateTime().toString("MM-dd hh:mm:ss zzz");
	QString strLabel = data.strName + " " + data.strStationName + " " + strTime;

	int nBufferSize = 0;

	switch (nId)
	{


	case 1://蒙皮破损
		//ui.label_1->setLable(strLabel);
		nBufferSize = m_pShell_broken->pustImage(data);
		break;

	case 2://水壶破损
		//ui.label_2->setLable(strLabel);
		nBufferSize = m_pKettle_broken->pustImage(data);
		break;

	case 3://顶部异物
		//ui.label_3->setLable(strLabel);
		nBufferSize = m_pTop_invade->pustImage(data);
		break;

	case 4://框架变形
		//ui.label_4->setLable(strLabel);
		nBufferSize = m_pFrame_deform->pustImage(data);
		break;

	case 5://电池倾斜
		//ui.label_5->setLable(strLabel);
		nBufferSize = m_pFrame_tilt->pustImage(data);
		break;

	case 6:
		//ui.label_6->setLable(strLabel);
		nBufferSize = m_pJX_steel_rope_broken->pustImage(data);
		break;
	case 7:
		//ui.label_7->setLable(strLabel);
		m_pJX_rope_coiler_failure->pustImage(data);
		break;
	case 8://8.融青断股
		//ui.label_8->setLable(strLabel);
		nBufferSize = m_RQ_steel_rope_broken->pustImage(data);
		break;
	case 9://9.融青漏油
		//ui.label_9->setLable(strLabel);
		nBufferSize = m_pRQ_oil_leakage->pustImage(data);
		break;

	case 10:// 测试kafka服务
		m_pKafkaState->setText("kafka server OK");
		m_pKafkaState->setStyleSheet("QLabel{background-color:rgb(10,200,10);}");
		break;

	default:
		break;
	}

	QString str = QString::number(nId) + " buffer size  " + QString::number(nBufferSize);
	m_pKafkaState->setText(str);
}



void PatrolServer::on_updateShow(int nId, QImage image, int nBufferSize)
{
	QString strTime = QDateTime::currentDateTime().toString("MM-dd hh:mm:ss zzz");

	QString strLabel = strTime + "  bufferSize " + QString::number(nBufferSize) + "   ;";

	switch (nId)
	{


	case 1://蒙皮破损
		ui.label_1->setLable(strLabel);
		ui.label_1->setImage(image);
		break;

	case 2://水壶破损
		ui.label_2->setLable(strLabel);
		ui.label_2->setImage(image);
		break;

	case 3://顶部异物
		ui.label_3->setLable(strLabel);
		ui.label_3->setImage(image);
		break;

	case 4://框架变形
		ui.label_4->setLable(strLabel);
		ui.label_4->setImage(image);
		break;

	case 5://电池倾斜
		ui.label_5->setLable(strLabel);
		ui.label_5->setImage(image);
		break;

	case 6:
		ui.label_6->setLable(strLabel);
		ui.label_6->setImage(image);
		break;
	case 7:
		ui.label_7->setLable(strLabel);
		ui.label_7->setImage(image);
		break;
	case 8://8.融青断股
		ui.label_8->setLable(strLabel);
		ui.label_8->setImage(image);
		break;
	case 9:
		ui.label_9->setLable(strLabel);
		ui.label_9->setImage(image);
		break;

	default:
		break;
	}

}



void PatrolServer::on_btnConfig_clicked()
{
	// 	qWarning() << "UI Id " << QThread::currentThreadId();
	QString str = "{\"server_url\": \"http://43.137.4.113/inspect_camera_capture\", \"station_id\": \"100532\", \"station_name\": \"\\u5185\\u8499\\u4e4c\\u6d77\\u65b0\\u897f\\u7ebf1\\u53f7\\u6362\\u7535\\u7ad9\", \"device_sn\": \"ND6B4094B2FO\", \"camera_id\": 12, \"camera_name\": \"\\u7535\\u6c60\\u4ed3\\u901a\\u9053\\u5916\", \"time_stamp\": 1734267196, \"picture_path\": \"ND6B4094B2FO_12_1734267196.jpg\", \"topic\": \"jiuxing_rope_coiler_failure\"}";

	// 	for (int i = 0; i < 1; i++)
	// 	{
	// 		on_ValueUpdate("jiuxing_steel_rope_broken", str);
	// 	}
		//return;

	configView* pDlg = new configView(m_strConfigPath);

	pDlg->m_strKettleDetModelPath = m_strKettleDetModelPath;
	pDlg->m_strKettleRectDefectModelPath = m_strKettleRectDefectModelPath;
	pDlg->m_strShellDetModelPath = m_strShellDetModelPath;
	pDlg->m_strShellRectDefectModelPath = m_strShellRectDefectModelPath;
	pDlg->m_strRQ_SteelDetModelPath = m_strRQ_SteelDetModelPath;
	pDlg->m_strRQ_MotorDetModelPath = m_strRQ_MotorDetModelPath;
	pDlg->m_strRQ_FloorDetModelPath = m_strRQ_FloorDetModelPath;
	pDlg->m_strRQ_MotorRectClassModelPath = m_strRQ_MotorRectClassModelPath;

	pDlg->m_strShellRectClassModelPath = m_strShellRectClassModelPath;
	pDlg->m_strTopRectClassModelPath = m_strTopRectClassModelPath;


	pDlg->m_strJX_SteelDetModelPath = m_strJX_SteelDetModelPath;
	pDlg->m_strJX_CableDetModelPath = m_strJX_CableDetModelPath;
	pDlg->m_strFrameDetModelPath = m_strFrameDetModelPath;
	pDlg->m_strTopDetModelPath = m_strTopDetModelPath;
	pDlg->m_strTopRectDefectModelPath = m_strTopRectDefectModelPath;

	// 检测模型地址
	pDlg->m_strRQ_MotorRectDefectModelPath = m_strRQ_MotorRectDefectModelPath;
	pDlg->m_strRQ_FloorRectDefectModelPath = m_strRQ_FloorRectDefectModelPath;
	pDlg->m_strRQ_SteelRectDefectModelPath = m_strRQ_SteelRectDefectModelPath;

	pDlg->showValue();
	pDlg->setModal(true);
	pDlg->show();

}


void PatrolServer::on_btnStopKafka_clicked()
{
	if (m_pKafka != nullptr)
	{
		m_pKafka->stop();
	}
}